home *** CD-ROM | disk | FTP | other *** search
- /*
- * timer.c
- *
- * Appshell Timer Handler
- * Copyright (C) 1991 Kompositor Software
- * Written by John F Wiederhirn
- *
- * Based on the AppShell Notification Handler
- * Copyright (C) 1990 Commodore-Amiga, Inc.
- * Written by David N. Junod
- *
- */
-
- /* The normal AppShell and system of includes... */
-
- #include <exec/exec.h>
- #include <devices/timer.h>
- #include <libraries/appshell.h>
- #include <exec/types.h>
- #include <exec/memory.h>
- #include <exec/libraries.h>
- #include <intuition/intuition.h>
- #include <graphics/gfx.h>
- #include <libraries/gadtools.h>
- #include <libraries/appshell.h>
- #include <utility/tagitem.h>
- #include <clib/alib_protos.h>
- #include <clib/alib_stdio_protos.h>
- #include <clib/exec_protos.h>
- #include <clib/intuition_protos.h>
- #include <clib/graphics_protos.h>
- #include <clib/gadtools_protos.h>
- #include <clib/appshell_protos.h>
- #include <clib/utility_protos.h>
- #include <pragmas/appshell.h>
- #include <string.h>
- #include "ae:support/misc_protos.h"
-
- #include "timer.h"
-
- extern struct Library *AppShellBase;
-
- /* Grabbing this will embed the version information generated */
- /* by BUMPREV in the make process into the actual executable. */
- #include "timer_rev.h"
-
- /* This is the global Timer handler information packet. */
- struct TimerInfo
- {
- struct timerequest *ti_TR; /* Template timerequest */
- };
-
- /* The TimerObject structure holds all the information specific to a */
- /* given timer event request. It also contains pointers to a dispatch */
- /* function and the actual timer event request (along with a duplicate */
- /* timeval struct to reset the request in a TH_TICKING situation. */
- struct TimerObject
- {
- struct timerequest ti_TR; /* Embedded. */
- struct timeval ti_TimeVal; /* Embedded, holds the length to wait */
- ULONG ti_FuncID; /* The app FuncID to execute */
- ULONG ti_Mode; /* Either ticking or one-shot */
- struct MHObject *ti_MHO; /* Pointer back to parent */
- };
-
- /* timer.c: function prototypes */
- BOOL open_timer (struct AppInfo *, struct MsgHandler *, struct TagItem *);
- BOOL handle_timer (struct AppInfo *, struct MsgHandler *, struct TagItem *);
- BOOL close_timer (struct AppInfo *, struct MsgHandler *, struct TagItem *);
- BOOL shutdown_timer (struct AppInfo *, struct MsgHandler *, struct TagItem *);
-
- /* The following comment block is in Autodoc format. This is a stringent
- * standard, do NOT modify the format!!! */
-
- /****** timer.lib/setup_timerA *********************************************
- *
- * NAME
- * setup_timerA - Initializes the timer message handler.
- *
- * SYNOPSIS
- * mh = setup_timerA (ai, tl)
- * D0 D0 D1
- *
- * struct MsgHandler *mh;
- * struct AppInfo *ai;
- * struct TagItem *tl;
- *
- * FUNCTION
- * This is the low-level function used to initialize the timer message
- * handler.
- *
- * NOTE: This call should not be called directly by the application,
- * but should invoked by using the APSH_AddHandler tag.
- *
- * There are no tags recognized at startup time.
- *
- * Valid tagitems to use at OPEN time:
- *
- * APSH_TimerMode, <timer mode>
- * This determines the what the handler returns to the user
- * application. Recognizes:
- *
- * TH_TICKING Repeated timer events, with frequency
- * based on content of APSH_TimerLen
- *
- * TH_ONESHOT One-shot alarm after duration given
- * in APSH_TimerLen
- *
- * APSH_TimerLen, <timeval>
- * Sets the duration of the timer command. Data is a pointer
- * to a timeval structure (see timer.device).
- *
- * APSH_CmdID, <function ID>
- * Function to execute when timer triggered. Examine APSH_NameTag
- * to get the ID code for the timer request.
- *
- *
- * INPUTS
- * ai - Pointer to the application's AppInfo structure.
- * tl - Pointer to an array of TagItem attributes for the
- * function.
- *
- * RESULT
- * mh - Pointer to a MsgHandler structure.
- *
- * BUGS
- *
- * SEE ALSO
- *
- **************************************************************************
- * Created: 21-May-91, John F Wiederhirn
- *
- */
-
- struct MsgHandler * ASM
- setup_timerA (REG(d0) struct AppInfo * ai, REG(d1) struct TagItem * tl)
- {
- struct MsgHandler *mh;
- struct MHObject *mho;
- struct TimerInfo *md;
-
- ULONG msize;
-
- /* This determines the amount of memory needed to hold the */
- /* "anchor" information for a msg handler */
- msize = sizeof (struct MsgHandler) +
- sizeof (struct TimerInfo) +
- (4L * sizeof (ULONG));
-
- /* Attempt to allocate the structure. If it fails, fall */
- /* through to a panic condition. */
- if (mh = (struct MsgHandler *) AllocVec (msize, MEMFLAGS))
- {
- /* mho gets set to point to the embedded MHObject */
- /* structure inside the MsgHandler struct we allocated... */
- mho = &(mh->mh_Header);
-
- /* ...and the node embedded inside the MHObject struct */
- /* needs to be set so that the system list handling */
- /* functions can identify the owner. */
- mho->mho_Node.ln_Type = APSH_MH_HANDLER_T;
- mho->mho_Node.ln_Pri = APSH_MH_HANDLER_P;
- mho->mho_Node.ln_Name = "TIMER";
-
- /* Initialize the list anchor. All timer requests */
- /* will be attached to this list anchor. List is */
- /* a standard doubly-linked Exec list. */
- NewList (&(mho->mho_ObjList));
-
- /* This is just our personal message handler ID code */
- /* so that AppShell can identify who is the owner of */
- /* messages which get passed to it by Intuition. */
- mho->mho_ID = APSH_TIMER_ID;
-
- /* get a pointer to the instance data */
- mho->mho_SysData = md = MEMORY_FOLLOWING (mh);
-
- /* The array of function pointers is set up so that */
- /* AppShell can dispatch messages as needed to the */
- /* proper Timer Handler functions. */
- mh->mh_NumFuncs = 4;
- mh->mh_Func = MEMORY_FOLLOWING (md);
- mh->mh_Func[APSH_MH_OPEN] = open_timer;
- mh->mh_Func[APSH_MH_HANDLE] = handle_timer;
- mh->mh_Func[APSH_MH_CLOSE] = close_timer;
- mh->mh_Func[APSH_MH_SHUTDOWN] = shutdown_timer;
-
- /* Create a standard Exec IPC message port. Since the */
- /* port is private to the AppShell-based application */
- /* and only AppShell will be sending messages to it, we */
- /* don't need to bother giving it a name... */
- if (mh->mh_Port = CreatePort (NULL, NULL))
- {
- /* ...but we do need to make sure that AppShell is */
- /* aware of which bit is going to be the trigger for */
- /* incoming message notification. This is done by */
- /* duplicating the mask of the port in the MsgHandler */
- /* field designed for it. */
- mh->mh_SigBits = (1L << mh->mh_Port->mp_SigBit);
-
- /* Create the template timerequest */
- if (md->ti_TR = (struct timerequest *)
- CreateExtIO (mh->mh_Port, sizeof (struct timerequest)))
- {
- /* Open the timer.device */
- if (!(OpenDevice ("timer.device", NULL, (struct IORequest *) md->ti_TR, UNIT_WAITUNTIL)))
- {
- struct timerequest *treq = md->ti_TR;
-
- /* Also, the node embedded in the timerrequest needs */
- /* to indicate the IPC port which should be notified. */
- treq->tr_node.io_Message.mn_ReplyPort = mh->mh_Port;
-
- /* Set the command to tell the system a timer event */
- /* is being requested, and to add it to the queue. */
- treq->tr_node.io_Command = TR_ADDREQUEST;
-
- /* All the non-dynamic setup for the message handler */
- /* is complete. Return a pointer to the valid */
- /* MsgHandler structure. */
- return (mh);
- }
-
- /* Delete the template timerequest */
- DeleteExtIO ((struct IORequest *) md->ti_TR);
- md->ti_TR = NULL;
- }
-
- DeletePort (mh->mh_Port);
- mh->mh_Port = NULL;
-
- /* Unable to allocate things */
- ai->ai_Pri_Ret = RETURN_FAIL;
- ai->ai_Sec_Ret = APSH_CLDNT_CREATE_PORT;
- ai->ai_TextRtn = PrepText (ai, APSH_MAIN_ID, ai->ai_Sec_Ret, NULL);
- }
- else
- {
- /* The system was unable to allocate the IPC port. */
- /* We set the proper flags to inform AppShell of the */
- /* failure, and prepare the error message we want */
- /* AppShell to display to the user (In this case, */
- /* the stock "Timer Handler couldn't create port!" */
- /* AppShell message). */
- ai->ai_Pri_Ret = RETURN_FAIL;
- ai->ai_Sec_Ret = APSH_CLDNT_CREATE_PORT;
- ai->ai_TextRtn = PrepText (ai, APSH_MAIN_ID, ai->ai_Sec_Ret, NULL);
- }
-
- /* The memory allocated for the MsgHandler structure can */
- /* be deallocated now. Just to make it clear, we also clear */
- /* the mh and mho pointers for easy debugging. */
- FreeVec ((APTR) mh);
- mho = NULL;
- mh = NULL;
- }
- else
- {
- /* Things are pretty bad. The attempt to allocate the memory for */
- /* the MsgHandler failed. Chances are, the system will not be able */
- /* to put up any requesters, but we set the AppShell flag to */
- /* indicate we failed, and prepare the message (stock AppShell */
- /* "Not Enough Memory!" message). */
- ai->ai_Pri_Ret = RETURN_FAIL;
- ai->ai_Sec_Ret = APSH_NOT_ENOUGH_MEMORY;
- ai->ai_TextRtn = PrepText (ai, APSH_MAIN_ID, ai->ai_Sec_Ret, NULL);
- }
-
- /* Return NULL to indicate failure just in case something besides */
- /* AppShell is watching entry/exit conditions. */
- return (NULL);
-
- }
-
- BOOL open_timer (struct AppInfo * ai, struct MsgHandler * mh, struct TagItem * tl)
- {
- struct MHObject *mho = &(mh->mh_Header);
- struct TimerInfo *md = mho->mho_SysData;
- struct TimerObject *tinf;
- struct timerequest *treq;
- struct MHObject *mob;
- BOOL retval = FALSE;
- struct timeval *tv;
- STRPTR nam1, nam2;
- ULONG mode, tsize;
- LONG FuncID;
-
- /* The list of tags pointed to by the tl pointer contain the key-value */
- /* pairs for the request to be queued. This is done in an array of */
- /* standard Exec TagItems. */
-
- /* The _NameTag TagItem needs to contain a pointer to a string */
- /* which has the unique name for the request. Since this is a */
- /* required parameter, the default is NULL. Also, because the */
- /* "data side" of a TagItem key-value set is defined as a long, */
- /* we need to cast it to a STRPTR. */
- nam1 = (STRPTR) GetTagData (APSH_NameTag, NULL, tl);
-
- /* The _CmdID Tag contains the AppShell ID number for the Application */
- /* function to be executed when the timer event occurs. Again, the */
- /* Tag is required so the default is NULL. ID numbers happen to be */
- /* longs as well, so no cast. */
- FuncID = GetTagData (APSH_CmdID, NULL, tl);
-
- /* The last of the required Tags, the _TimerLen should contain a */
- /* pointer to a standard timer.device timeval structure which sets */
- /* the number of seconds and microseconds until the event. */
- /* */
- /* NOTE: Timing hasnt been saturation tested yet, so for the time */
- /* being, this Handler is certified only for "second" granularity. */
- tv = (struct timeval *) GetTagData (APSH_TimerLen, NULL, tl);
-
- /* The optional _TimerMode Tag contains a mode setting for the */
- /* event to be queued. The default value is TH_TICKING, which */
- /* causes a recurrent event stream from the request. TH_ONESHOT */
- /* as mode would cause a single event from the request. */
- mode = GetTagData (APSH_TimerMode, TH_TICKING, tl);
-
- /* This makes sure the required Tags were all present. If not, */
- /* the function simply returns a FALSE value. */
- if (nam1 && FuncID && tv)
- {
- /* Here, the mode is checked to verify it falls within the */
- /* range of acceptable values. Failure case just falls */
- /* through to a FALSE return right now. */
- if ((mode > TH_STRTMODE) && (mode < TH_ENDMODE))
- {
- /* Calculate the amount of memory needed to hold all */
- /* of the structs and data related to a given request... */
- tsize = sizeof (struct MHObject) +
- sizeof (struct TimerObject) +
- strlen (nam1) + 1L;
-
- /* ...and allocate them. Failure simply falls through */
- /* to a FALSE return currently. This will eventually set */
- /* up an error condition similar to the failed allocation */
- /* error condition in setup_timerA. The first structure */
- /* is the MHObject so it gets the pointer returned from */
- /* the allocation and casts it to the proper type. */
- if (mob = (struct MHObject *) AllocVec (tsize, MEMFLAGS))
- {
- /* The rest of the structure pointers can now be aimed */
- /* to the proper offsets inside the allocated block and */
- /* cast to the proper type. */
- tinf = (struct TimerObject *) MEMORY_FOLLOWING (mob);
- tinf->ti_MHO = mob;
- nam2 = (STRPTR) MEMORY_FOLLOWING (tinf);
-
- /* The embedded Exec node structure needs to be setup */
- /* so the system knows who owns it. The values get */
- /* set to the assigned Timer Handler values. */
- mob->mho_Node.ln_Type = APSH_MH_HANDLER_T;
- mob->mho_Node.ln_Pri = APSH_MH_HANDLER_P;
- mob->mho_Node.ln_Name = nam2;
-
- /* A field of the request's MHObject structure is set */
- /* to point to the related TimerObject. We need this */
- /* later to find everything... */
- mob->mho_SysData = tinf;
-
- /* Set the parent object */
- mob->mho_Parent = mho;
-
- /* The MHObject is declared to belong to the Timer */
- /* Handler, so AppShell can dispatch properly when. */
- /* the event occurs. */
- mob->mho_ID = APSH_TIMER_ID;
-
- /* Copy the contents pointed at by the _NameTag Tag */
- /* into our own memory. */
- strcpy (nam2, nam1);
-
- /* And finally, attach the request to the list of */
- /* requests which are anchored by the MHObject that */
- /* is embedded in the MsgHandler structure. */
- AddTail (&(mho->mho_ObjList), &(mob->mho_Node));
-
- /* Copy over the values from the template (p.874 L&D RKM) */
- tinf->ti_TR = *md->ti_TR;
-
- /* Get a pointer to the embedded timerequest */
- treq = &(tinf->ti_TR);
-
- /* Initialize the timerequest structure with the */
- /* values for the seconds and microseconds to wait */
- /* until triggering the event. */
- treq->tr_time.tv_secs = tinf->ti_TimeVal.tv_secs = tv->tv_secs;
- treq->tr_time.tv_micro = tinf->ti_TimeVal.tv_micro = tv->tv_micro;
-
- /* Initialize the TimerObject structure with the */
- /* information passed in the Tag array and where */
- /* our handler should call to redispatch to the */
- /* function given by the application. */
- tinf->ti_Mode = mode;
- tinf->ti_FuncID = FuncID;
-
- /* Everything has been intialized. The request is */
- /* placed in the system queue, and finally the retval */
- /* var is changed so the function returns TRUE. */
- SendIO ((struct IORequest *) treq);
-
- /* Increment the number of outstanding messages */
- mh->mh_Outstanding++;
-
- retval = TRUE;
- }
- }
- }
-
- /* Whatever the retval var holds at this point, that's what the */
- /* AppShell dispatcher gets back. */
- return (retval);
- }
-
- BOOL handle_timer (struct AppInfo * ai, struct MsgHandler * mh, struct TagItem * tl)
- {
- struct MHObject *mho = &(mh->mh_Header);
- struct TimerObject *tinf;
- struct timerequest *treq;
- struct timeval *tv;
- LONG FuncID;
-
- /* Pull the messages from the port */
- while (tinf = (struct TimerObject *) GetMsg (mh->mh_Port))
- {
- /* Decrement the number of outstanding messages */
- mh->mh_Outstanding--;
-
- /* Set the current object */
- mho->mho_CurNode = tinf->ti_MHO;
-
- /* This ugly little declaration/initialization just */
- /* pulls the ID number of the application function to */
- /* be called from where it was stashed in the TimerObject */
- /* structure. */
- FuncID = tinf->ti_FuncID;
-
- /* Since the function ID comes from the application, its */
- /* not safe to assume it will be meaningful. AppShell can */
- /* handle the case where the function ID doesnt point to */
- /* an existing app function, but this cannot be detected */
- /* by the dispatcher so gets checked here. NO_FUNCTION is */
- /* the legal value for a null function table ID. */
- if (FuncID != NO_FUNCTION)
- {
- /* This Tag array, like the FuncID var, doesnt need */
- /* to hang around long, so local is fine. */
- struct TagItem attrs[2];
-
- /* All these do is inform the dispatcher where the */
- /* dispatch came from (a handler), and who owns it */
- /* (the Timer handler). */
- attrs[0].ti_Tag = APSH_Handler;
- attrs[0].ti_Data = APSH_TIMER_ID;
- attrs[1].ti_Tag = TAG_DONE;
-
- /* This is the entry point for the AppShell dispatcher */
- /* for external dispatch requests. It looks up the ID */
- /* in its tables and dispatches to that function. The */
- /* NULL is because we are passing no command line along */
- /* to the function. */
- PerfFunc (ai, FuncID, NULL, NULL);
- }
-
- /* treq to the timerequest struct stashed in the TimerObject... */
- treq = (struct timerequest *) & (tinf->ti_TR);
-
- /* and tv to the timeval struct embedded in the TimerObject. */
- tv = (struct timeval *) & (tinf->ti_TimeVal);
-
- /* When DispatchUser returns, this determines the mode so */
- /* that the request can be requeued or terminated. */
- switch (tinf->ti_Mode)
- {
- /* Just as it looks, requeue the request for another */
- /* run. Since the timer device trashes the values in */
- /* the actual timerequest for how long to wait, they */
- /* get re-initialized from the duplicate copies in the */
- /* TimerObject structure. Then the request is sent via */
- /* the generic SendIO function back into the timer device */
- /* queue. */
- case TH_TICKING:
- treq->tr_time.tv_secs = tv->tv_secs;
- treq->tr_time.tv_micro = tv->tv_micro;
- SendIO ((struct IORequest *) treq);
-
- /* Increment the number of outstanding messages */
- mh->mh_Outstanding++;
- break;
-
- /* In this case, the request can be terminated, so it */
- /* gets passed to close_timer() for deallocation. The node */
- /* pointer gets reused here. The two NULLs are to */
- /* satify the prototype. */
- case TH_ONESHOT:
- close_timer (ai, mh, NULL);
- break;
- }
- }
-
- /* Clear the current object, now that it is out there in the TwiZone */
- mho->mho_CurNode = NULL;
-
- return (TRUE);
- }
-
- BOOL close_timer (struct AppInfo * ai, struct MsgHandler * mh, struct TagItem * tl)
- {
- struct MHObject *mho = &(mh->mh_Header);
- struct List *mhl = &(mho->mho_ObjList);
- struct MHObject *mob = NULL;
- struct TimerObject *tinf;
- struct timerequest *treq;
- struct timeval *tv;
- BOOL retval = FALSE;
- STRPTR nam1;
-
- /* See if they specified an event to close */
- if (nam1 = (STRPTR) GetTagData (APSH_NameTag, NULL, tl))
- {
- /* Locate the node which contains the timer request. */
- /* Since the node will always be the very first part */
- /* of the MHObject it is related to, we can create a */
- /* pointer to the MHObject with a simple cast. */
- mob = (struct MHObject *) FindName (mhl, nam1);
- }
- /* No name was provided, so close the current object */
- else
- {
- /* Get a pointer to the current object */
- mob = mho->mho_CurNode;
- }
-
- /* See if we have an object to close */
- if (mob)
- {
- /* Once again, all the pointers need to be aimed at the proper */
- /* elements of the request. */
- tinf = (struct TimerObject *) mob->mho_SysData;
- treq = (struct timerequest *) & (tinf->ti_TR);
- tv = (struct timeval *) & (tinf->ti_TimeVal);
-
- /* Since this can be called by close_timer with the request */
- /* still outstanding, test if it is. */
- if (~CheckIO ((struct IORequest *) treq))
- {
- /* Decrement the number of outstanding messages */
- mh->mh_Outstanding--;
-
- /* Its outstanding. Use the generic AbortIO function */
- /* to force the system to abort the request. */
- AbortIO ((struct IORequest *) treq);
-
- /* This needs to wait until the system tells it that */
- /* the request has been aborted. */
- WaitIO ((struct IORequest *) treq);
- }
-
- /* This unlinks the request from within our list of requests. */
- Remove ((struct Node *) mob);
-
- /* And finally, all the memory allocated in open_timer can be given */
- /* back to the system. */
- FreeVec (mob);
-
- /* Indicate success */
- retval = TRUE;
- }
-
- return (retval);
- }
-
- BOOL shutdown_timer (struct AppInfo * ai, struct MsgHandler * mh, struct TagItem * tl)
- {
- struct MHObject *mho = &(mh->mh_Header);
- struct TimerInfo *md = mho->mho_SysData;
- struct List *list = &(mho->mho_ObjList);
- struct Node *node, *nxtnode;
-
- /* Remove the message handler from the AppShell list */
- Remove ((struct Node *) mh);
-
- /* Get rid of any outstanding timer requests */
-
- /* Make sure there are entries in the list */
- if (list->lh_TailPred != (struct Node *) list)
- {
- /* Let's start at the very beginning... */
- node = list->lh_Head;
-
- /* Continue while there are still nodes */
- while (nxtnode = node->ln_Succ)
- {
- /* Make this the current node */
- mho->mho_CurNode = (struct MHObject *) node;
-
- /* Close the current node */
- close_timer (ai, mh, NULL);
-
- /* Go on to the next node */
- node = nxtnode;
- }
- }
-
- /* Close the timer device */
- CloseDevice ((struct IORequest *) md->ti_TR);
-
- /* Delete the template timerequest */
- DeleteExtIO ((struct IORequest *) md->ti_TR);
-
- /* At this point, the IPC port will receive no further */
- /* messages from the timer device as all requests are */
- /* deleted. This implies all messages have been */
- /* ReplyMsg'd as well, so its now safe to close the */
- /* port. */
- DeletePort (mh->mh_Port);
-
- /* Everything else is deallocated so this just returns */
- /* the memory allocated in setup_timerA back to the */
- /* system. */
- FreeVec (mh);
-
- return (TRUE);
- }
-
- /* END OF TIMER.C */
-